<template>
  <div class="upload-file-box">
    <div class="input-line">
      <div class="input-title">
        <span class="name">附件上传：</span>
      </div>
      <label v-if="loading">
        <div class="ban-button icon-btn">
          <span>附件上传</span>
        </div>
      </label>
      <label for="files" v-else>
        <div class="upload-btn icon-btn">
          <span>附件上传</span>
        </div>
      </label>
      <input type="file" @change="changeFiles" class="files" id="files" />
    </div>
    <div class="limit-info">
      支持文件格式：常用文件格式，单个文件不能超过5Mb
    </div>
    <div class="input-line" v-show="loading">
      <div class="input-title">
        <span class="name">上传状态：</span>
      </div>
      <div class="loading-box">
        <div class="current-loading" :style="{ width: percent + '%' }"></div>
      </div>
      <div class="percent-val">{{ Math.round(percent) }}%</div>
      <div class="cancel-btn" @click="cancelUpload">取消</div>
    </div>
    <div class="file-state" v-show="loading">
      <div class="file-name">
        <span>{{ originalName }}</span>
      </div>
      <div class="state">
        <span v-loading="uploading">正在上传</span>
      </div>
    </div>
    <div class="file-state" v-show="complete">
      <div class="file-name">
        <a :href="filePath" target="_blank">{{ originalName }}</a>
      </div>
      <div class="state" v-show="!!filePath">
        <span>已完成</span>
      </div>
    </div>
  </div>
</template>
<script>
import axios from 'axios'
import SparkMD5 from "spark-md5";
export default {
  data() {
    return {
      source: null,
      fileName: "",
      uploading: false,
      percent: 0,
      originalName: "",
      md5: "",
      uploadState: false,
      filePath: "",
      loading: false,
      complete: false,
      CHUNK_SIZE : 5 * 1024 * 1024,
      uploadedChunks:[],
    }
  },
  methods: {
    getFileChunks(file){
      let CHUNK_SIZE = this.CHUNK_SIZE;

      const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
      const chunks = [];
      for (let i = 0; i < chunkCount; i++) {
        const start = i * CHUNK_SIZE;
        const end = Math.min(file.size, start + CHUNK_SIZE);
        chunks.push(file.slice(start, end));
      }

      return chunks;
    },
    changeFiles(e) {
      let files = e.srcElement.files;
      if (files && files.length) {
        let  file = files[0];
        this.originalName = file.name;
        this.source = axios.CancelToken.source();

        this.loading = true;
        this.uploading = true;
        this.complete = false;
        this.calculateMD5(file).then(()=>{
            let md5 = this.md5;
          this.$http.post('/res/file/init?fileName=' + file.name+'&fileMd5='+md5, {
            fileName: file.name,
            fileMd5: md5 // 文件MD5计算
          }).then(res=>{
            let uploadId= res.data.data;

            // 2. 分块上传

            const chunks = this.getFileChunks(file);
            const total = chunks.length;
            let uploaded = 0;
            Promise.all(chunks.map((chunk, index) => {
              const formData = new FormData();
              formData.append('chunk', chunk, `chunk_${index}`);
              formData.append('index', index);
              formData.append('uploadId', uploadId);
              formData.append('fileMd5', md5);

              return axios.post('/res/file/chunk', formData, {
                headers: {'Content-Type': 'multipart/form-data'},
                onUploadProgress: progress => {
                  // 更新进度条
                  const percent = ((uploaded * 100) / total).toFixed(1);
                  this.percent = percent;
                  console.log('----------percent----------------',percent)
                }
              }).then(() => uploaded++);
            })).then(res=>{
              // 3. 触发合并
              axios.post('/res/file/merge?fileName=' + file.name+"&uploadId=" + uploadId+'&fileMd5='+md5, {
                fileName: file.name,
                uploadId,
                md5
              }).then(()=>{
                this.percent=100;
                this.uploading = false;
              });
            });
          }).catch(err=>{
            this.$router.push({path:'/login',query:{}});
            console.log(err)
          });

          console.log('-resres-----',res);
        })


      }
      document.getElementById("files").value = null;
    },
    readFile(chunk, fileReader) {
      return new Promise((resolve, reject) => {
        fileReader.onload = (e) => {
          resolve(e.target.result);
        };
        fileReader.onerror = reject;
        fileReader.readAsArrayBuffer(chunk);
      });
    }
    ,
    async calculateMD5(file) {
      const chunkSize = 1024 * 1024; // 分块大小为 1MB
      const spark = new SparkMD5.ArrayBuffer();
      const fileReader = new FileReader();
      const totalChunks = Math.ceil(file.size / chunkSize);
      let currentChunk = 0;

      while (currentChunk < totalChunks) {
        const start = currentChunk * chunkSize;
        const end = Math.min(start + chunkSize, file.size);
        const chunk = file.slice(start, end);

        // 异步读取文件块并计算 MD5
        const arrayBuffer = await this.readFile(chunk, fileReader);
        spark.append(arrayBuffer);
        currentChunk++;
      }

      // 获取最终的 MD5 值
      this.md5 = spark.end();
    },


    cancelUpload() {
      //取消上传
      this.source.cancel("Operation canceled by the user.");
    },
  },
}
</script>
<style >
.upload-file-box {
  padding-top: 30px;
  padding-left: 20px;
  .input-line {
    height: 40px;
    .input-title {
      float: left;
      line-height: 40px;
      font-size: 14px;
      text-align: right;
      width: 80px;
      .text {
        color: #666;
      }
    }
    .files {
      display: none;
    }
    .input {
      float: left;
      width: 400px;
      margin-left: 16px;
    }
    .icon-btn {
      margin-top: 5px;
      margin-left: 16px;
      float: left;
      height: 30px;
      line-height: 30px;
      padding-right: 10px;
      padding-left: 10px;
      background-color: #0099ff;
      border-radius: 5px;
      color: #fff;
      font-size: 14px;
      cursor: pointer;
      transition: all 0.35s;
    }
    .upload-btn {
      &:hover {
        opacity: 0.8;
      }
    }
    .ban-button {
      cursor: not-allowed;
      background-color: #eeeff3;
    }
    .loading-box {
      margin-top: 15px;
      float: left;
      width: 270px;
      height: 10px;
      background-color: #f6f6f6;
      border-radius: 5px;
      margin-left: 15px;
      .current-loading {
        height: 10px;
        border-radius: 5px;
        background-color: #3399cc;
        /*width: 50%;*/
      }
    }
    .percent-val {
      line-height: 40px;
      width: 60px;
      text-align: center;
      font-size: 14px;
      color: #666;
      float: left;
    }
    .cancel-btn {
      line-height: 40px;
      font-size: 14px;
      color: #999;
      text-decoration: underline;
      cursor: pointer;
      user-select: none;
      &:hover {
        color: #3399CC;
      }
    }
    .del-btn {
      line-height: 40px;
      font-size: 14px;
      color: #999;
      text-decoration: underline;
      cursor: pointer;
      user-select: none;
      &:hover {
        color: #3399CC;
      }
    }
  }
  .limit-info {
    color: #999;
    font-size: 14px;
    padding-left: 96px;
    line-height: 34px;
    height: 34px;
  }
  .file-state {
    width: 270px;
    margin-left: 95px;
    overflow: hidden;
    .file-name {
      float: left;
      width: 210px;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      font-size: 14px;
      color: #999;
      a {
        color: #999;
        font-size: 14px;
        text-decoration: none;
        &:hover {
          color: #3399cc;
        }
      }
    }
    .state {
      float: right;
      font-size: 14px;
      color: #1db396;
    }
  }
}
</style>
